home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Tool Chest / Apple Developer CD Series Tool Chest February 1996 (Apple Computer)(1996).iso / Tool Chest / Text / WASTE / WASTE 1.2a2 / WELowLevelEditing.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-10-12  |  24.8 KB  |  903 lines  |  [TEXT/CWIE]

  1. /*
  2.  *    WELowLevelEditing.c
  3.  *
  4.  *    WASTE PROJECT
  5.  *  Low-Level Editing Routines
  6.  *
  7.  *  Copyright (c) 1993-1995 Marco Piovanelli
  8.  *    All Rights Reserved
  9.  *
  10.  *  C port by Dan Crevier
  11.  *
  12.  */
  13.  
  14.  
  15. #include "WASTEIntf.h"
  16.  
  17. pascal Boolean _WEIsWordRange(long rangeStart, long rangeEnd, WEHandle hWE)
  18. {
  19.     // _WEIsWordRange returns TRUE if the specified range is a word range,
  20.     // i.e. if it would be possible to select it by double-clicking and (optionally) dragging.
  21.  
  22.     long wordStart, wordEnd;
  23.  
  24.     // determine if rangeStart is at the beginning of a word
  25.     WEFindWord(rangeStart, kLeadingEdge, &wordStart, &wordEnd, hWE);
  26.     if (rangeStart == wordStart) 
  27.     {
  28.  
  29.         // determine if rangeEnd is at the end of a word
  30.         WEFindWord(rangeEnd, kTrailingEdge, &wordStart, &wordEnd, hWE);
  31.         return (rangeEnd == wordEnd);
  32.     }
  33.     return false;
  34. } // _WEIsWordRange
  35.  
  36. pascal Boolean _WEIsPunct(long offset, WEHandle hWE)
  37. {
  38.     short cType;
  39.     
  40.     cType = WECharType(offset, hWE);
  41.     if ((cType & smcTypeMask) == smCharPunct) 
  42.     {
  43.         cType &= smcClassMask;
  44.         if ((cType == smPunctNormal) || (cType == smPunctBlank)) 
  45.             return true;
  46.     }
  47.     return false;
  48. }  // _WEIsPunct
  49.  
  50. pascal void _WEIntelligentCut(long *rangeStart, long *rangeEnd, WEHandle hWE)
  51. {
  52.  
  53.     // _WEIntelligentCut is called by other WASTE routines to determine the actual
  54.     // range to be deleted when weFIntCutAndPaste is enabled.
  55.     // On entry, rangeStart and rangeEnd specify the selection range visible to the user.
  56.     // On exit, rangeStart and rangeEnd specify the actual range to be removed.
  57.  
  58.     // do nothing if the intelligent cut-and-paste feature is disabled
  59.     if (!BTST((*hWE)->features, weFIntCutAndPaste)) 
  60.         return;
  61.  
  62.     // intelling cut-&-paste rules should be applied only to word ranges...
  63.     if (_WEIsWordRange(*rangeStart, *rangeEnd, hWE) == false) 
  64.         return;
  65.  
  66.     // ...without punctuation characters at the beginning or end
  67.     if (_WEIsPunct(*rangeStart, hWE)) 
  68.         return;
  69.     if (_WEIsPunct(*rangeEnd - 1, hWE)) 
  70.         return;
  71.  
  72.     // if the character preceding the selection range is a space, discard it
  73.     if (WEGetChar(*rangeStart - 1, hWE) == kSpace) 
  74.         (*rangeStart)--;
  75.     // else, if the character following the selection range is a space, discard it
  76.     else if (WEGetChar(*rangeEnd, hWE) == kSpace)
  77.         (*rangeEnd)++;
  78.  
  79. } // _WEIntelligentCut
  80.  
  81. pascal short _WEIntelligentPaste(long rangeStart, long rangeEnd, WEHandle hWE)
  82. {
  83.     short retval;
  84.     
  85.     // _WEIntelligentPaste is called by other WASTE routines to determine whether
  86.     // an additional space character should be added (before or after) after inserting
  87.     // new text (usually from the Clipboard or from a drag).
  88.  
  89.     retval = weDontAddSpaces;
  90.  
  91.     // do nothing unless the intelligent cut-and-paste feature is enabled
  92.     if (!BTST((*hWE)->features, weFIntCutAndPaste)) 
  93.         return retval;
  94.  
  95.     // extra spaces will be added only if the pasted text looks like a word range,
  96.     // without punctuation characters at the beginning or at the end
  97.     if (_WEIsPunct(rangeStart, hWE))
  98.         return retval;
  99.     if (_WEIsPunct(rangeEnd - 1, hWE))
  100.         return retval;
  101.  
  102.     // if the character on the left of the pasted text is a punctuation character
  103.     // and the character on the right isn't,  add a space on the right, and vice versa
  104.     if (_WEIsPunct(rangeStart - 1, hWE))
  105.     {
  106.         if (_WEIsPunct(rangeEnd, hWE) == false) 
  107.             retval = weAddSpaceOnRightSide;
  108.     }
  109.     else if (_WEIsPunct(rangeEnd, hWE))
  110.         retval = weAddSpaceOnLeftSide;
  111.  
  112.     return retval;
  113. } // _WEIntelligentPaste
  114.  
  115. pascal OSErr _WEInsertRun(long runIndex, long offset, long styleIndex, WEPtr pWE)
  116. {
  117.  
  118.     // Insert a new element in the style run array, at the specified runIndex position.
  119.     // The new element consists of the pair <offset, styleIndex>.
  120.  
  121.     RunArrayElement element;
  122.     OSErr err;
  123.  
  124.     // prepare the element record to be inserted in the array
  125.     element.runStart = offset;
  126.     element.styleIndex = styleIndex;
  127.  
  128.     // do the insertion
  129.     if ((err = _WEInsertBlock((Handle) pWE->hRuns, (const void *) &element, sizeof(element), (runIndex + 1) * sizeof(element))) != noErr)
  130.         return err;
  131.  
  132.     // increment style run count
  133.     pWE->nRuns++;
  134.  
  135.     // increment the reference count field of the style table element
  136.     // referenced by the newly inserted style run
  137.     (*pWE->hStyles)[styleIndex].refCount++;
  138.  
  139.     return noErr;
  140. } // _WEInsertRun
  141.  
  142. pascal void _WERemoveRun(long runIndex, WEPtr pWE)
  143. {
  144.     StyleTableElementPtr pStyle;
  145.     
  146.     // get a pointer to the style table element referenced by the style run
  147.     pStyle = *pWE->hStyles + (*pWE->hRuns)[runIndex].styleIndex;
  148.  
  149.     // decrement the reference count field of the style table element
  150.     // referenced by the style run to be removed
  151.     pStyle->refCount--;
  152.  
  153. #if WASTE_OBJECTS
  154.     // dispose of the embedded object (if any)
  155.     if (pStyle->info.runStyle.tsObject != NULL)
  156.         _WEFreeObject(pStyle->info.runStyle.tsObject);
  157. #endif
  158.     
  159.     // remove a "slot" from the run array
  160.     _WERemoveBlock((Handle) pWE->hRuns, sizeof(RunArrayElement), runIndex * sizeof(RunArrayElement));
  161.  
  162.     // decrement style run count
  163.     pWE->nRuns--;
  164.     
  165. } // _WERemoveRun
  166.  
  167. pascal void _WEChangeRun(long runIndex, long newStyleIndex, Boolean keepOld, WEPtr pWE)
  168. {
  169.     // change the styleIndex field of the specified element of the style run array
  170.  
  171.     long oldStyleIndex;
  172.     StyleTableElementPtr oldStyle, newStyle;
  173.  
  174.     // do the change
  175.     oldStyleIndex = (*pWE->hRuns)[runIndex].styleIndex;
  176.     (*pWE->hRuns)[runIndex].styleIndex = newStyleIndex;
  177.     
  178.     // get pointers to old and new style table elements
  179.     oldStyle = *pWE->hStyles + oldStyleIndex;
  180.     newStyle = *pWE->hStyles + newStyleIndex;
  181.     
  182.     // increment the reference count field of the new style table element
  183.     newStyle->refCount++;
  184.     
  185.     // decrement the reference count field of the old style table element
  186.     oldStyle->refCount--;
  187.  
  188. #if WASTE_OBJECTS
  189.     // dispose of the embedded object (if any) unless it is again referenced in the new style
  190.     if (!keepOld)
  191.     {
  192.         WEObjectDescHandle oldObject = oldStyle->info.runStyle.tsObject;
  193.         
  194.         if ((oldObject != NULL) && (oldObject != newStyle->info.runStyle.tsObject))
  195.             _WEFreeObject(oldObject);
  196.     }
  197. #endif
  198.  
  199. } // _WEChangeRun
  200.  
  201. pascal OSErr _WENewStyle(const WETextStyle *ts, long *styleIndex, WEPtr pWE)
  202. {
  203.     // given the specified WETextStyle record, find the corresponding entry
  204.     // in the style table (create a new entry if necessary), and return its index
  205.  
  206.     StyleTableElementPtr pElement;
  207.     StyleTableElement element;
  208.     long index;
  209.     long unusedIndex;
  210.     OSErr err;
  211.  
  212.     // see if the given style already exists in the style table
  213.     // while scanning the table, also remember the position of the first unused style, if any
  214.     unusedIndex = -1;
  215.     pElement = *pWE->hStyles;
  216.     for ( index = 0 ; index < pWE->nStyles ; index++ )
  217.     {
  218.         // check for entries which aren't referenced and can be recycled
  219.         if (pElement->refCount == 0)
  220.             unusedIndex = index;
  221.  
  222.         // perform a bitwise comparison between the current element and the specified style
  223.         // (actually, we ignore metrics information)
  224.         else if (_WEBlockCmp(&pElement->info.runStyle, ts, sizeof(WETextStyle)))
  225.         {
  226.             // found: style already present
  227.             *styleIndex = index;
  228.             return noErr;
  229.         }
  230.         pElement++;
  231.     } // for
  232.  
  233.     // the specified style doesn't exist in the style table
  234.     // since this is a new entry, we have to calculate font metrics information
  235.     element.info.runStyle = *ts;
  236.     _WEFillFontInfo(pWE->port, &element.info);
  237.     
  238.     // set the high bit of tsFlags if attributes specify a right-to-left run
  239.     if (BTST(pWE->flags, weFBidirectional))
  240.     {
  241.         if (GetScriptVariable(FontToScript(element.info.runStyle.tsFont), smScriptRight) != 0)
  242.         {
  243.             BSET(element.info.runStyle.tsFlags, tsRightToLeft);
  244.         }
  245.     }
  246.     
  247.     // see if we can recycle an unused entry
  248.     if (unusedIndex >= 0) 
  249.     {
  250.         index = unusedIndex;
  251.         (*pWE->hStyles)[index].info = element.info;
  252.     }
  253.     else
  254.     {
  255.         // no reusable entry: we have to append a new element to the table
  256.         element.refCount = 0;
  257.         if ((err = _WEInsertBlock((Handle) pWE->hStyles, &element, sizeof(element), index * sizeof(element))) != noErr)
  258.             return err;
  259.  
  260.         // update style count in the WE record
  261.         pWE->nStyles++;
  262.     }
  263.     
  264.     // return the index to the new element
  265.     *styleIndex = index;
  266.     return noErr;
  267. }
  268.  
  269. pascal OSErr _WERedraw(long rangeStart, long rangeEnd, WEHandle hWE)
  270. {
  271.     WEPtr pWE = *hWE;        // assume WE record is already locked
  272.     LineArrayPtr pLines;
  273.     long startLine, endLine;
  274.     long oldTextHeight, newTextHeight;
  275.     LongRect r;
  276.     Rect viewRect, updateRect;
  277.     RgnHandle saveClip;
  278.     GrafPtr savePort;
  279.     OSErr err;
  280. #if WASTE_REDRAW_SPEED
  281.     LongRect scrollRect;
  282.     RgnHandle updateRgn,
  283.               utilRgn;
  284. #endif
  285.  
  286.     // do nothing if recalculation has been inhibited
  287.     if (!BTST(pWE->features, weFInhibitRecal)) 
  288.     {
  289.         // hide the caret
  290. #if WASTE_REDRAW_SPEED
  291.         BCLR(pWE->flags, weFCaretVisible);
  292. #else
  293.         if (BTST(pWE->flags, weFCaretVisible))
  294.         { 
  295.             _WEBlinkCaret(hWE);
  296.         }
  297. #endif
  298.  
  299.         // remember total text height
  300.         oldTextHeight = pWE->destRect.bottom - pWE->destRect.top;
  301.  
  302.         // find line range affected by modification
  303.         startLine = WEOffsetToLine(rangeStart, hWE);
  304.         endLine = WEOffsetToLine(rangeEnd, hWE);
  305.  
  306.         // recalculate line breaks starting from startLine
  307.         if ((err = _WERecalBreaks(&startLine, &endLine, hWE)) != noErr) 
  308.         {
  309.             goto cleanup;
  310.         }
  311.  
  312.         // recalculate slops
  313.         _WERecalSlops(startLine, endLine, hWE);
  314.  
  315.         // calculate new total text height
  316.         newTextHeight = pWE->destRect.bottom - pWE->destRect.top;
  317.  
  318.         // calculate the rectangle to redraw (in long coordinates)
  319.         r.left = -SHRT_MAX;
  320.         r.right = SHRT_MAX;
  321.         pLines = *pWE->hLines;
  322.         r.top = pLines[startLine].lineOrigin;
  323.  
  324. #if WASTE_REDRAW_SPEED
  325.         // if total text height hasn't changed, it's enough to redraw lines up to endLine
  326.         // otherwise we must redraw all lines from startLine on
  327.  
  328.         if (endLine < pWE->nLines - 1)
  329.              r.bottom = pLines[endLine + 1].lineOrigin;
  330.         else
  331.             r.bottom = newTextHeight;
  332.         WEOffsetLongRect(&r, 0, pWE->destRect.top);
  333.  
  334.         if (newTextHeight == oldTextHeight)
  335.             WELongRectToRect(&r, &updateRect);
  336.         else
  337.         {
  338.             /*      Instead of scrolling the lines below the deleted text up by redrawing them,
  339.              *      use scroll bits to move the displayed text up.
  340.              */
  341.  
  342.             scrollRect = pWE->viewRect;
  343.             if (newTextHeight > oldTextHeight)
  344.                 scrollRect.top = pLines[startLine + 1].lineOrigin + pWE->destRect.top;
  345.             else
  346.                 scrollRect.top = pLines[startLine].lineOrigin + pWE->destRect.top;
  347.             WELongRectToRect(&scrollRect, &updateRect);
  348.             updateRgn = NewRgn();
  349.             ScrollRect(&updateRect, 0, newTextHeight - oldTextHeight, updateRgn);
  350.  
  351.             /*      Redraw the exposed region (caused by a scroll up)       */
  352.  
  353.             WELongRectToRect(&r, &updateRect);
  354.             utilRgn = NewRgn();
  355.             RectRgn(utilRgn, &updateRect);
  356.             DiffRgn(updateRgn, utilRgn, updateRgn);
  357.             DisposeRgn(utilRgn);
  358.             WEUpdate(updateRgn, hWE);
  359.             DisposeRgn(updateRgn);
  360.         }
  361. #else
  362.         // if total text height hasn't changed, it's enough to redraw lines up to endLine
  363.         // otherwise we must redraw all lines from startLine on
  364.         if ((newTextHeight == oldTextHeight) && (endLine < pWE->nLines - 1)) 
  365.         {
  366.             r.bottom = pLines[endLine + 1].lineOrigin;
  367.         }
  368.         else if (newTextHeight < oldTextHeight) 
  369.         {
  370.             r.bottom = oldTextHeight;
  371.         }
  372.         else
  373.         {
  374.             r.bottom = newTextHeight;
  375.         }
  376.         
  377.         WEOffsetLongRect(&r, 0, pWE->destRect.top);
  378.  
  379.         // calculate the intersection between this rectangle and the view rectangle
  380.         WELongRectToRect(&r, &updateRect);
  381. #endif
  382.         WELongRectToRect(&pWE->viewRect, &viewRect);
  383.  
  384.         if (SectRect(&updateRect, &viewRect, &updateRect)) 
  385.         {
  386.             // set up the port and the clip region
  387.             GetPort(&savePort);
  388.             SetPort(pWE->port);
  389.  
  390.             // set the clip region to updateRect
  391.             saveClip = NewRgn();
  392.             GetClip(saveClip);
  393.             ClipRect(&updateRect);
  394.  
  395.             // we only really need to redraw the visible lines
  396.             startLine = _WEPixelToLine(updateRect.top - pWE->destRect.top, hWE);
  397.             endLine = _WEPixelToLine(updateRect.bottom - pWE->destRect.top - 1, hWE);
  398.  
  399.             // redraw the lines (pass TRUE in the doErase parameter)
  400.             _WEDrawLines(startLine, endLine, true, hWE);
  401.  
  402.             // erase the portion of the update rectangle below the last line (if any)
  403.             pLines = *pWE->hLines;
  404.             updateRect.top = pWE->destRect.top + pLines[endLine + 1].lineOrigin;
  405.             if (updateRect.top < updateRect.bottom) 
  406.             {
  407.                 EraseRect(&updateRect);
  408.             }
  409.  
  410.             // restore the clip region
  411.             SetClip(saveClip);
  412.             DisposeRgn(saveClip);
  413.  
  414.             // restore the port
  415.             SetPort(savePort);
  416.  
  417.             // redraw the caret or the selection range
  418.             if (pWE->selStart < pWE->selEnd) 
  419.             {
  420.                 _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  421.             }
  422.             else
  423.             {
  424.                 _WEBlinkCaret(hWE);
  425.             }
  426.         } // if SectRect
  427.  
  428.         // scroll the selection range into view
  429.         WESelView(hWE);
  430.     } // if recal not inhibited
  431.  
  432.     // clear result code
  433.     err = noErr;
  434.  
  435. cleanup:
  436.     // return result code
  437.     return err;
  438. } // _WERedraw
  439.  
  440. pascal OSErr WECalText(WEHandle hWE)
  441. {
  442.     Boolean saveWELock;
  443.     OSErr err;
  444.  
  445.     // lock WE record
  446.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  447.  
  448. #if WASTE_WECALTEXT_DOES_REDRAW
  449.  
  450.     // recalculate line breaks & slops and redraw the text
  451.     err = _WERedraw(0, LONG_MAX, hWE);
  452.  
  453. #else
  454.     
  455.     {
  456.         long startLine = 0;
  457.         long endLine = LONG_MAX;
  458.  
  459.         // recalculate line breaks & slops without redrawing anything
  460.         if ((err = _WERecalBreaks(&startLine, &endLine, hWE)) == noErr)
  461.             _WERecalSlops(startLine, endLine, hWE);
  462.     }
  463.  
  464. #endif
  465.  
  466.     // unlock the WE record
  467.     _WESetHandleLock((Handle) hWE, saveWELock);
  468.  
  469.     // return result code
  470.     return err;
  471. }
  472.  
  473. pascal OSErr _WESetStyleRange(long rangeStart, long rangeEnd, short mode, const WETextStyle *ts, WEHandle hWE)
  474. {
  475.     // alter the style attributes of the specified text range according to ts and mode
  476.  
  477.     WEPtr pWE = *hWE;                    // assume WE record is already locked
  478.     RunArrayHandle hRuns = pWE->hRuns;
  479.     long offset;
  480.     long runIndex;
  481.     long oldStyleIndex, newStyleIndex;
  482.     WERunInfo runInfo;
  483.     char continuousStyles;
  484.     OSErr err;
  485.  
  486.     WEASSERT(rangeStart < rangeEnd, "\pBad style range");
  487.  
  488.     // if mode contains weDoToggleFace, we need to determine which QuickDraw styles
  489.     // are continuous over the specified text range: those styles must be turned off
  490.     if (BTST(mode, kModeToggleFace)) 
  491.     {
  492.         short temp = weDoFace;
  493.         _WEContinuousStyleRange(rangeStart, rangeEnd, &temp, &runInfo.runAttrs.runStyle, hWE);
  494.         continuousStyles = runInfo.runAttrs.runStyle.tsFace;
  495.     }
  496.     else
  497.     {
  498.         continuousStyles = 0;
  499.     }
  500.  
  501.     // find the index to the first style run in the specified range
  502.     offset = rangeStart;
  503.     runIndex = _WEOffsetToRun(offset, hWE);
  504.  
  505.     // run thru all the style runs that encompass the selection range
  506.     do
  507.     {
  508.         // find style index for this run and retrieve corresponding style attributes
  509.         oldStyleIndex = (*hRuns)[runIndex].styleIndex;
  510.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  511.  
  512.         // _WEGetIndStyle returns textLength + 1 in runInfo.runEnd for the last style run:
  513.         // correct this anomaly (which is useful for other purposes, anyway)
  514.         if (runInfo.runEnd > pWE->textLength) 
  515.         {
  516.             runInfo.runEnd = pWE->textLength;
  517.         }
  518.         
  519.         // apply changes to existing style attributes as requested
  520.         _WECopyStyle(ts, &runInfo.runAttrs.runStyle, continuousStyles, mode);
  521.  
  522.         // get a style index for the new text style
  523.         if ((err = _WENewStyle(&runInfo.runAttrs.runStyle, &newStyleIndex, pWE)) != noErr)
  524.             goto cleanup;
  525.         
  526.         // if offset falls on a style boundary and this style run has become identical
  527.         // to the previous one, merge the two runs together
  528.         if ((offset == runInfo.runStart) && (runIndex > 0) &&
  529.             ((*hRuns)[runIndex - 1].styleIndex == newStyleIndex))
  530.         {
  531.             _WERemoveRun(runIndex, pWE);
  532.             runIndex--;
  533.         }
  534.  
  535.         // style index changed?
  536.         if (oldStyleIndex != newStyleIndex) 
  537.         {
  538.             // if offset is in the middle of a style run, insert a new style run in the run array
  539.             if (offset > runInfo.runStart) 
  540.             {
  541.                 if ((err = _WEInsertRun(runIndex, offset, newStyleIndex, pWE)) != noErr) 
  542.                 {
  543.                     goto cleanup;
  544.                 }
  545.                 runIndex++;
  546.             }
  547.             else
  548.             {
  549.                 // otherwise just change the styleIndex field of the current style run element
  550.                 _WEChangeRun(runIndex, newStyleIndex, (rangeEnd < runInfo.runEnd), pWE);
  551.             }
  552.  
  553.             // if specified range ends in the middle of a style run, insert yet another element
  554.             if (rangeEnd < runInfo.runEnd) 
  555.             {
  556.                 if ((err = _WEInsertRun(runIndex, rangeEnd, oldStyleIndex, pWE)) != noErr) 
  557.                 {
  558.                     goto cleanup;
  559.                 }
  560.             }
  561.         } // if oldStyle != newStyle
  562.  
  563.         // go to next style run
  564.         runIndex++;
  565.         offset = runInfo.runEnd;
  566.  
  567.     } while (offset < rangeEnd);
  568.  
  569.     // if the last style run ends exactly at the end of the specified range,
  570.     // see if we can merge it with the following style run
  571.     if ((offset == rangeEnd) && (runIndex < pWE->nRuns) && 
  572.         ((*hRuns)[runIndex].styleIndex == newStyleIndex)) 
  573.     {
  574.         _WERemoveRun(runIndex, pWE);
  575.     }
  576.  
  577.     // clear result code
  578.     err = noErr;
  579.  
  580. cleanup:
  581.     // return result code
  582.     return err;
  583. } // _WESetStyleRange
  584.  
  585. pascal OSErr _WEApplyStyleScrap(long rangeStart, long rangeEnd, StScrpHandle styleScrap, WEHandle hWE)
  586. {
  587.     // apply the given style scrap to the specified text range
  588.  
  589.     WEPtr pWE = *hWE;    // assume WE record is already locked
  590.     TEStyleScrapElement *pElement;
  591.     long runStart, runEnd;
  592.     short index, lastElement;
  593.     WETextStyle ts;
  594.     OSErr err;
  595.  
  596.     // loop through each element of the style scrap
  597.     lastElement = (*styleScrap)->scrpNStyles - 1;
  598.     for(index = 0; index <= lastElement; index++)
  599.     {
  600.         // get a pointer to the current scrap element
  601.         pElement = (TEStyleScrapElement *) ((*styleScrap)->scrpStyleTab + index);
  602.  
  603.         // calculate text run to which this element is to be applied
  604.         runStart = rangeStart + pElement->scrpStartChar;
  605.         if (index < lastElement) 
  606.         {
  607.             runEnd = rangeStart + pElement[1].scrpStartChar;
  608.         }
  609.         else
  610.         {
  611.             runEnd = rangeEnd;
  612.         }
  613.  
  614.         // perform some range checking
  615.         if (runEnd > rangeEnd) 
  616.         {
  617.             runEnd = rangeEnd;
  618.         }
  619.         if (runStart >= runEnd) 
  620.         {
  621.             continue;
  622.         }
  623.  
  624.         // copy style to a local variable in case memory moves
  625.         * (TextStyle *) &ts = pElement->scrpTEAttrs.runTEStyle;
  626.  
  627.         // apply the specified style to the range
  628.         if ((err = _WESetStyleRange(runStart, runEnd, weDoAll + weDoReplaceFace, &ts, hWE)) != noErr) 
  629.         {
  630.             return err;
  631.         }
  632.     }
  633.     return noErr;
  634. } // _WEApplyStyleScrap
  635.  
  636. #if WASTE_OBJECTS
  637.  
  638. pascal OSErr _WEApplySoup(long offset, Handle hSoup, WEHandle hWE)
  639. {
  640.     WESoupPtr pSoup;
  641.     long pSoupEnd;
  642.     WETextStyle ts;
  643.     Handle hObjectData;
  644.     long objectOffset;
  645.     Boolean saveWELock;
  646.     OSErr err;
  647.  
  648.     BLOCK_CLR(ts);
  649.     hObjectData = NULL;
  650.  
  651.     // lock the WE record
  652.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  653.  
  654.     // lock the soup in high heap
  655.     HLockHi(hSoup);
  656.     pSoup = (WESoupPtr)*hSoup;
  657.     pSoupEnd = (long)pSoup + GetHandleSize(hSoup);
  658.  
  659.     // loop through each object descriptor in the soup
  660.     while((long)pSoup < pSoupEnd)
  661.     {
  662.         // if soupDataSize is negative, this soup is a special type that we won't handle here
  663.         if (pSoup->soupDataSize < 0)
  664.             continue;
  665.  
  666.         // create a new relocatable block the hold the object data
  667.         if ((err = _WEAllocate(pSoup->soupDataSize, kAllocTemp, &hObjectData)) != noErr) 
  668.             goto cleanup;
  669.  
  670.         // copy the object data to this block
  671.         BlockMoveData((Ptr)(pSoup + 1), *hObjectData, pSoup->soupDataSize);
  672.  
  673.         // create a new object out of the tagged data
  674.         if ((err = _WENewObject(pSoup->soupType, hObjectData, hWE, &ts.tsObject)) != noErr) 
  675.             goto cleanup;
  676.  
  677.         // if there was no new handler for this object, use the object size stored in the soup
  678.         if ((*ts.tsObject)->objectTable == NULL)
  679.             (*ts.tsObject)->objectSize = pSoup->soupSize;
  680.  
  681.         // record a reference to the object descriptor in the style table
  682.         objectOffset = pSoup->soupOffset + offset;
  683.         err = _WESetStyleRange(objectOffset, objectOffset + 1, weDoObject, &ts, hWE);
  684.         hObjectData = NULL;
  685.         ts.tsObject = NULL;
  686.         if (err != noErr) 
  687.             goto cleanup;
  688.  
  689.         // advance soup pointer
  690.         pSoup = (WESoupPtr)((long)pSoup + sizeof(WESoup) + pSoup->soupDataSize);
  691.  
  692.     } // while
  693.  
  694.     // clear result code
  695.     err = noErr;
  696.  
  697. cleanup:
  698.     // clean up
  699.     HUnlock(hSoup);
  700.     _WEForgetHandle((Handle *) &ts.tsObject);
  701.     _WEForgetHandle(&hObjectData);
  702.  
  703.     // unlock the WE record
  704.     _WESetHandleLock((Handle) hWE, saveWELock);
  705.  
  706.     // return result code
  707.     return err;
  708.  
  709. } // _WEApplySoup
  710.  
  711. #endif    // WASTE_OBJECTS
  712.  
  713. pascal void _WEBumpRunStart(long runIndex, long deltaRunStart, WEPtr pWE)
  714. {
  715.     // add deltaLineStart to the lineStart field of all line records
  716.     // starting from lineIndex
  717.  
  718.     RunArrayElementPtr pRun = *pWE->hRuns + runIndex;
  719.     long nRuns = pWE->nRuns;
  720.  
  721.     // loop through the style run array adjusting the runStart fields
  722.     for ( ; runIndex <= nRuns; runIndex++ )
  723.     {
  724.         pRun->runStart += deltaRunStart;
  725.         pRun++;
  726.     }
  727. } // _WEBumpRunStart
  728.  
  729. pascal void _WERemoveRunRange(long rangeStart, long rangeEnd, WEHandle hWE)
  730. {
  731.     // the range of text between rangeStart and rangeEnd is being deleted
  732.     // update the style run array (and the style table) accordingly
  733.  
  734.     WEPtr pWE = *hWE;    // assume WE record is already locked
  735.     RunArrayElementPtr pRun;
  736.     long startRun, endRun;
  737.  
  738.     // find the index to the first and last style runs in the specified range
  739.     startRun = _WEOffsetToRun(rangeStart, hWE);
  740.     endRun = _WEOffsetToRun(rangeEnd, hWE) - 1;
  741.  
  742.     // remove all style runs between startRun and endRun
  743.     for ( ; endRun > startRun; endRun-- )
  744.         _WERemoveRun(endRun, pWE);
  745.     
  746.     // move back all subsequent style runs
  747.     _WEBumpRunStart(startRun + 1, rangeStart - rangeEnd, pWE);
  748.  
  749.     if ((endRun == startRun) && (endRun < pWE->nRuns - 1))
  750.     {
  751.         pRun = *pWE->hRuns + endRun;
  752.         pRun[1].runStart = rangeStart;
  753.     }
  754.  
  755.     // remove the first style run if is has become zero length
  756.     pRun = *pWE->hRuns + startRun;
  757.     if (pRun[0].runStart == pRun[1].runStart) 
  758.     {
  759.         _WERemoveRun(startRun, pWE);
  760.         startRun--;
  761.     }
  762.  
  763.     // merge the first and last runs if they have the same style index
  764.     if (startRun >= 0) 
  765.     {
  766.         pRun = *pWE->hRuns + startRun;
  767.         if (pRun[0].styleIndex == pRun[1].styleIndex) 
  768.         {
  769.             _WERemoveRun(startRun + 1, pWE);
  770.         }
  771.     }
  772. } // _WERemoveRunRange
  773.  
  774. pascal void _WEBumpLineStart(long lineIndex, long deltaLineStart, WEPtr pWE)
  775. {
  776.     // add deltaLineStart to the lineStart field of all line records
  777.     // starting from lineIndex
  778.  
  779.     LineRec *pLine = *pWE->hLines + lineIndex;
  780.     long nLines = pWE->nLines;
  781.  
  782.     // loop through the line array adjusting the lineStart fields
  783.     for ( ; lineIndex <= nLines; lineIndex++ )
  784.     {
  785.         pLine->lineStart += deltaLineStart;
  786.         pLine++;
  787.     }
  788. } // _WEBumpLineStart
  789.  
  790. pascal void _WERemoveLineRange(long rangeStart, long rangeEnd, WEHandle hWE)
  791. {
  792.     // the range of text between rangeStart and rangeEnd is being deleted
  793.     // update the line array accordingly
  794.  
  795.     WEPtr pWE = *hWE;    // assume WE record is already locked
  796.     long startLine, nLines;
  797.  
  798.     // remove all line records between rangeStart and rangeEnd
  799.     startLine = WEOffsetToLine(rangeStart, hWE) + 1;
  800.     nLines = WEOffsetToLine(rangeEnd, hWE) - startLine + 1;
  801.     
  802.     _WERemoveBlock((Handle) pWE->hLines, nLines * sizeof(LineRec), startLine * sizeof(LineRec));
  803.     pWE->nLines -= nLines;
  804.  
  805.     // update the lineStart field of all the line records that follow
  806.     _WEBumpLineStart(startLine, rangeStart - rangeEnd, pWE);
  807. }
  808.  
  809. pascal OSErr _WEDeleteRange(long rangeStart, long rangeEnd, WEHandle hWE)
  810. {
  811.     // used internally to delete a text range
  812.     WEPtr pWE = *hWE;    // assume WE record is already locked
  813.     WERunInfo runInfo;
  814.     OSErr err;
  815.  
  816.     if (rangeEnd > pWE->textLength)
  817.         rangeEnd = pWE->textLength;
  818.     
  819.     // do nothing if the specified range is empty
  820.     if (rangeStart == rangeEnd) 
  821.         goto cleanup1;
  822.     
  823.     // save the first style in the specified range in nullStyle
  824.     WEGetRunInfo(rangeStart, &runInfo, hWE);
  825.     pWE->nullStyle = runInfo.runAttrs;
  826.     BSET(pWE->flags, weFUseNullStyle);
  827.  
  828. #if WASTE_OBJECTS
  829.     // special case: if we're deleting up to the end of the text, see whether
  830.     // there's an embedded object at the very end and remove it
  831.     if (rangeEnd == pWE->textLength) 
  832.     {
  833.         WEGetRunInfo(rangeEnd - 1, &runInfo, hWE);
  834.         if (runInfo.runAttrs.runStyle.tsObject != NULL) 
  835.         {
  836.             runInfo.runAttrs.runStyle.tsObject = NULL;
  837.             if ((err = _WESetStyleRange(rangeEnd - 1, rangeEnd, weDoObject, &runInfo.runAttrs.runStyle, hWE)) != noErr) 
  838.                 goto cleanup2;
  839.         }
  840.     }
  841. #endif
  842.  
  843.     // remove all line records between rangeStart and rangeEnd
  844.     _WERemoveLineRange(rangeStart, rangeEnd, hWE);
  845.     
  846.     // remove all style runs between rangeStart and rangeEnd
  847.     _WERemoveRunRange(rangeStart, rangeEnd, hWE);
  848.     
  849.     // remove the text
  850.     _WERemoveBlock(pWE->hText, rangeEnd - rangeStart, rangeStart);
  851.  
  852.     // update textLength field
  853.     pWE->textLength -= (rangeEnd - rangeStart);
  854.  
  855. cleanup1:
  856.     // clear result code
  857.     err = noErr;
  858.  
  859. cleanup2:
  860.     // return result code
  861.     return err;
  862. } // _WEDeleteRange
  863.  
  864. pascal OSErr _WEInsertText(long offset, Ptr textPtr, long textLength, WEHandle hWE)
  865. {
  866.     WEPtr pWE = *hWE;    // assume WE record is already locked
  867.     short mode;
  868.     OSErr err;
  869.  
  870.     // do nothing if textLength is zero or negative
  871.     if (textLength <= 0)
  872.         return noErr;
  873.  
  874.     // insert the text
  875.     if ((err = _WEInsertBlock(pWE->hText, textPtr, textLength, offset)) != noErr)
  876.         return err;
  877.     
  878.     // update the lineStart fields of all lines following the insertion point
  879.     _WEBumpLineStart(WEOffsetToLine(offset, hWE) + 1, textLength, pWE);
  880.  
  881.     // update the runStart fields of all style runs following the insertion point
  882.     _WEBumpRunStart(_WEOffsetToRun(offset - 1, hWE) + 1, textLength, pWE);
  883.  
  884.     // update the textLength field
  885.     pWE->textLength += textLength;
  886.  
  887.     // make sure the newly inserted text doesn't reference any embedded object
  888. #if WASTE_OBJECTS
  889.     pWE->nullStyle.runStyle.tsObject = NULL;
  890. #endif
  891.     mode = weDoObject;
  892.  
  893.     // if there is a valid null style, apply it to the newly inserted text
  894.     if (BTST(pWE->flags, weFUseNullStyle))
  895.         mode += (weDoAll + weDoReplaceFace);
  896.  
  897.     if ((err = _WESetStyleRange(offset, offset + textLength, mode, &pWE->nullStyle.runStyle, hWE)) != noErr)
  898.         return err;
  899.  
  900.     return noErr;
  901.  
  902. } // _WEInsertText
  903.